
/***  DOCUMENTATION LAYER

	Generic Javascript Class File
	* for use with class.ini.js
	
	Last Update: Apr 2007
	Author: Tom at klenwell@gmail.com
	License: GPL License (http://www.opensource.org/licenses/gpl-license.php)

FUNCTIONS	
	ini = function(id)
	_create_element = function(type, id, class_name)
	_event_cache = function()
	attach_event = function(id, type, fx)
	add_element = function(parentNode, type, id, class_name)
	set_html = function(id, html)
	set_style = function(id, style)
	add_stylesheet = function(_href)
	toggle_display = function(id)
	publish = function(parent_id)
	clear = function()
	reveal_tree = function(id)

NOTES
	Base class should be defined in the class.ini.js file, which autoloads this
	file.  The class init method can then be called to initialize the object.  Other
	methods can be added to this file. 
	
	To link: <script type="text/javascript" src="path/to/file.js"></script>

REFERENCE
	http://klenwell.googlecode.com/svn/trunk/JS/jini/
	http://developer.mozilla.org/en/docs/JavaScript

______________________________________________________________________________*/


// method: ini
/*____________________________________________________________________________*/
DomHandler.prototype.ini = function(id)
{
	// properties
	this.id = ( id == null ? 'dombuilder' : id);
	this.core_id = this.id + '_child';
	this.DOM_IDX = new Object();
	this.container = new Object();
	this.core = new Object();
	this.tree = '';
	
	// stylesheet
	this.stylesheet = new Object();
	this.stylesheet_added = 0;
	
	// build container
	this.container = this._create_element('div', this.id, 'container');
	this._update_idx(this.container);
	
	// add core node as child of container
	this.add_element(this.container, 'div', this.core_id, 'core');
	this.core = this.container.firstChild;
	
	// add unload event
	this.attach_event('window', 'unload', this._event_cache.flush);
	
	return;
};
/*____________________________________________________________________________*/


// method: _update_idx
/*____________________________________________________________________________*/
DomHandler.prototype._update_idx = function(Obj)
{
/* NOTE	
	Why, you may ask, this clumsy meta-structure?  Nothing is actually appended
	to the current document until the publish method is called.  Therefore, the
	customary DOM handles will not recognize anything in our DOM structure until that
	time.  This being the case, we must construct a mechanism to keep track of things
	until then.  Viola: this.DOM_IDX
*/

	var _id = Obj.id;
	var _pid = ( Obj.parentNode != null ) ? Obj.parentNode.id : null;
	this.DOM_IDX[_id] = { 'parent_id' : _pid, 'OBJECT' : Obj };

}; // end method
/*____________________________________________________________________________*/


// method: _create_element
/*____________________________________________________________________________*/
DomHandler.prototype._create_element = function(type, id, class_name)
{
	// debug
	//alert(type + ' / ' + id + ' / ' + class_name);
	
	// create object
	var NewEl = document.createElement(type);

	// set id
	NewEl.setAttribute('id', id);
	
	// set class / for info on IE bug, see : http://phosphorusandlime.blogspot.com/2007/04/js-ie-lacks-class.html
	if ( class_name != null ) 
	{
		NewEl.setAttribute('class', class_name);
		NewEl.className = class_name;
	}
	//var c = NewEl.getAttribute('class'); alert(class_name + ' -> ' + NewEl.className + ' / ' + c + ' / ' +  NewEl.id);

	// return	
	return NewEl;

}; // end method
/*____________________________________________________________________________*/


// method: event_cache
/*____________________________________________________________________________*/
DomHandler.prototype._event_cache = function()
{
	var listEvents = [];
	
	return {
		listEvents : listEvents,
		add : function(node, sEventName, fHandler) { listEvents.push(arguments); },
		flush : function() 
		{
			var i, item;
			for(i = listEvents.length - 1; i >= 0; i = i - 1)
			{
				item = listEvents[i];
				if( item[0].removeEventListener ) { item[0].removeEventListener(item[1], item[2], item[3]); };
				if( item[1].substring(0, 2) != "on" ) { item[1] = "on" + item[1]; };
				if( item[0].detachEvent ) { item[0].detachEvent(item[1], item[2]); };
				item[0][item[1]] = null;
			};
		}
	};
}(); // end method
/*____________________________________________________________________________*/


// method: attach_event
/*____________________________________________________________________________*/
DomHandler.prototype.attach_event = function(id, type, fx)
{
	if ( id == 'window' ) var obj = window;	
	else var obj = this.DOM_IDX[id]['OBJECT'];
	
	if ( obj.addEventListener ) 
	{
		obj.addEventListener( type, fx, false );
		this._event_cache.add(obj, type, fx);
	}
	else if ( obj.attachEvent ) 
	{
		obj["e"+type+fx] = fx;
		obj[type+fx] = function() { obj["e"+type+fx]( window.event ); }
		obj.attachEvent( "on"+type, obj[type+fx] );
		this._event_cache.add(obj, type, fx);
	}
	else
	{
		obj["on"+type] = obj["e"+type+fx];
	}

}; // end method
/*____________________________________________________________________________*/


// method: add_element
/*____________________________________________________________________________*/
DomHandler.prototype.add_element = function(parentNode, type, id, class_name)
{
	// default parentNode :
	if ( parentNode == null ) parentNode = this.container;

	// create element
	var AddEl = this._create_element(type, id, class_name);
	
	// append
	parentNode.appendChild(AddEl);
	
	// update DOM_IDX (removed)
	this._update_idx(AddEl);	

}; // end method
/*____________________________________________________________________________*/


// method: set_html
/*____________________________________________________________________________*/
DomHandler.prototype.set_html = function(id, html)
{
/* NOTE	
	It would make more sense to use document.getElementByIt[id] to update the 
	element's innerHTML prop and that was what I originally tried.  However, this
	fails because, I gather, the document will not recognize the id as part of its
	structure if the container has not yet been appended to it.  Therefore, we update 
	the object in the DOM_IDX, which will be referenced when the container is finally
	published.
*/

	// must be reflected in this.DOM_IDX
	if ( this.DOM_IDX[id] == null ) return;
	
	// change HTML	
	this.DOM_IDX[id]['OBJECT'].innerHTML = html;
	
	// debug
	//if ( this.debug == 1 ) this.DOM_IDX[id]['OBJECT'].debug('black');

}; // end method
/*____________________________________________________________________________*/


// method: set_style
/*____________________________________________________________________________*/
DomHandler.prototype.set_style = function(id, style)
{
	/* see note above in set_html for logic here */

	// must be reflected in this.DOM_IDX
	if ( this.DOM_IDX[id] == null ) return;
	
	// return current style
	var _old_style = this.DOM_IDX[id]['OBJECT'].getAttribute('style');
	
	// change style attribute	
	this.DOM_IDX[id]['OBJECT'].setAttribute('style', style);

	return _old_style;

}; // end method
/*____________________________________________________________________________*/


// method: add_stylesheet
/*____________________________________________________________________________*/
DomHandler.prototype.add_stylesheet = function(_href)
{
	// if added, change src
	if ( this.stylesheet_added == 1 )
	{
		alert('stylesheet already added?');
		this.stylesheet.setAttribute('href', _href);
	}
	
	// append to head
	else
	{
		this.stylesheet_added = 1;
		this.stylesheet = document.createElement('link');
		this.stylesheet.setAttribute('rel', 'stylesheet');
		this.stylesheet.setAttribute('type', 'text/css');
		this.stylesheet.setAttribute('href', _href);
		headNode = document.getElementsByTagName('head').item(0);
		headNode = headNode.appendChild(this.stylesheet);
	}

}; // end method
/*____________________________________________________________________________*/


// method: toggle_display
/*____________________________________________________________________________*/
AkwaForm.prototype.toggle_display = function(id)
{
	var obj = document.getElementById(id);
	if ( obj.style.display != 'none' ) obj.style.display = 'none';
	else obj.style.display = '';
}; // end method
/*____________________________________________________________________________*/


// method: publish
/*____________________________________________________________________________*/
DomHandler.prototype.publish = function(parent_id)
{
	if ( document.getElementById(parent_id) )
	{
		parentNode = document.getElementById(parent_id);
	}
	else
	{
		if ( parent_id != null ) alert('invalid parent_id ['+parent_id+'] -- will publish to document.body');
		parentNode = document.body;
	} 
	
	// to do: if container is already present, remove (this would allow republishing) 
	
	// append container
	parentNode.appendChild(this.container);

}; // end method
/*____________________________________________________________________________*/


// method: clear
/*____________________________________________________________________________*/
DomHandler.prototype.clear = function()
{
	// change HTML	
	this.set_html(this.core.id, '');

}; // end method
/*____________________________________________________________________________*/


// method: reveal_structure
/*____________________________________________________________________________*/
DomHandler.prototype.reveal_tree = function(id, lvl)
{
	var structure = '';
	var lm = '. ';	// level marker
	
	// defaults
	if ( id == null ) id = this.container.id;
	if ( lvl == null ) lvl = 0;
	if ( lvl > 12 ) { alert('looping much?'); die(); }
	
	// set level marker
	var _pre = '';
	for ( i=0; i<lvl; i++ ) _pre += lm;
	
	// get child nodes
	obj = this.DOM_IDX[id]['OBJECT'];
	
	// update tree
	var bg = document.getElementById(id).style.background;
	var over_evt = 'javascript:document.getElementById("'+id+'").style.background="#ffffcc";';
	var out_evt = 'javascript:document.getElementById("'+id+'").style.background="'+bg+'";';
	this.tree = this.tree + "<div onmouseover='"+over_evt+"' onmouseout='"+out_evt+"'>" + _pre + id + "</div>";
	
	// recurse your children!
	if ( obj.hasChildNodes() )
	{
		var cNodes = obj.childNodes;
		if ( this.debug ) alert('lvl ' + lvl + ' : ' + id + ' has ' + cNodes.length + ' child nodes' );
		lvl++;
		for ( var i=0; i<cNodes.length; i++ )
		{
			cn_id = cNodes.item(i).id;
			if ( 0 ) alert('cn_id (' + i + ') : ' + cn_id);
			if ( cn_id != null ) this.reveal_tree(cn_id, lvl);
		}
	}
	
	lvl--;
	return this.tree;
	
}; // end method
/*____________________________________________________________________________*/



